package com.database.bean;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.AttributeOverride;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

import com.util.FormatUtil;


@Entity
@Table(name="GAME")
@AttributeOverride( name="id", column = @Column(name="id") )
@XmlRootElement
public class Game extends BaseBean {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
		
	@Size(min=1, max=50)
	@Column(name="name")
	private String name=null;
	
	@Column(name="startDate")
	private Date startDate=null;
	
	@Column(name="initDate")
	private Date initDate=null;

	@Min(1)
	@Column(name="roundTime")
	private Double roundTime=null;
	
	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	@JoinColumn(name="gameId")
	private Set<User> users =new HashSet<User>();
	
	@ManyToMany(fetch = FetchType.EAGER, cascade={CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
	@JoinTable(name = "GAME_SUPPLIER", joinColumns = { @JoinColumn(name = "GAME_ID") }, inverseJoinColumns = { @JoinColumn(name = "SUPPLIER_ID") })
	private Set<Supplier> suppliers =new HashSet<Supplier>();
	
	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	@Fetch(value = FetchMode.SELECT)
    @JoinColumn(name="gameId")
	private List<Round> rounds=new ArrayList<Round>();

	//helper für Tableanzeige
	@Transient
	private List<User> userList=new ArrayList<User>();
	
	/**
	 * 
	 */
	public Game() {
		super();
	}

	public Game(String name, Date startDate, Date initDate, Double roundTime) {
		super();
		this.name = name;
		this.startDate = startDate;
		this.initDate = initDate;
		this.roundTime = roundTime;
	}



	public Game(String name, Date startDate, Double roundTime, Set<User> users,
			List<Round> rounds) {
		super();
		this.name = name;
		this.startDate = startDate;
		this.roundTime = roundTime;
		this.users = users;
		this.rounds = rounds;
	}



	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getStartDate() {
		return startDate;
	}

	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}

	public Double getRoundTime() {
		return roundTime;
	}

	public void setRoundTime(Double roundTime) {
		this.roundTime = roundTime;
	}
	
	public Date getInitDate() {
		return initDate;
	}

	public void setInitDate(Date initDate) {
		this.initDate = initDate;
	}


	public Set<User> getUsers() {
		this.initUserList();
		return this.users;
	}
	
	private void initUserList(){
		List<User> ulist=new ArrayList<User>();
		ulist.addAll(this.users);
		this.userList=ulist;
	}
	
	public List<User> getUserList() {
		if(this.users.size()!=this.userList.size())
			this.initUserList();
		return userList;
	}

	public void setUsers(Set<User> users) {
		this.users = users;
	}
	
	public String formattedDate(){
		if(this.startDate!=null)
			return FormatUtil.dateFormatter.format(this.startDate);
		else
			return "No Date";
	}
	
	/**
	 * @return the suppliers
	 */
	public Set<Supplier> getSuppliers() {
		return suppliers;
	}

	/**
	 * @param suppliers the suppliers to set
	 */
	public void setSuppliers(Set<Supplier> suppliers) {
		this.suppliers = suppliers;
	}

	public List<Round> getRounds() {
		return rounds;
	}

	public void setRounds(List<Round> rounds) {
		this.rounds = rounds;
	}

	public Round getLastRound(){
		Round round=null;
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(round==null){
					round=r;
				}
				else if (r.getRoundNumber()>round.getRoundNumber()){
					round=r;
				}
			}		
		}

		return round;
	}
	
	/**
	 * Get the last simulated round
	 * @return
	 */
	public Round getLastSimulatedRound(){
		Round round=null;
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(r.isPlayed() && round==null){
					round=r;
				}
				else if (r.isPlayed() && r.getRoundNumber()>round.getRoundNumber()){
					round=r;
				}
			}		
		}

		return round;
	}
	
	
	public Round getFirstRound(){
		Round round=null;
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(round==null){
					round=r;
				}
				else if (r.getRoundNumber()<round.getRoundNumber()){
					round=r;
				}
			}		
		}

		return round;
	}
	
	public boolean initializable(){
		Round round=null;
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(round==null){
					round=r;
				}
				else if (r.getRoundNumber()<round.getRoundNumber()){
					round=r;
				}
			}		
		}
		if(round==null)
			return false;
		else
			return !round.isPlayed();
	}
	
	
	/**
	 * Method to get the previous round for users to show them 
	 * which decisions they made before and also which results they had.
	 * @param actRound
	 * @return the previous round to show the users the previous decisions
	 */
	public Round getPreviousRoundFromRound(Round actRound){
		
		//if round==null then game not started
		//if roundNr==1 then first round and no previous Round
		//if roundNr>1 then there should be a previous round
		
		Round previousRound = null;
		int actRoundNr = 0;
		
		if(actRound!=null){
			actRoundNr = actRound.getRoundNumber();
			if(actRoundNr>1){
				previousRound = this.getRoundWithNr(actRoundNr-1);
			}
		}
		
		return previousRound;
	}
	
	/**
	 * Method to get the next round for users.
	 * @param actRound
	 * @return the next round
	 */
	public Round getNextRoundFromRound(Round actRound){
		
		//if round==null then game not started
		//if roundNr==lastRound then last round and no next Round
		//if roundNr<lastRound then there should be a next round
		
		Round nextRound = null;
		int actRoundNr = 0;
		
		if (actRound != null) {
			actRoundNr = actRound.getRoundNumber();
			nextRound = this.getRoundWithNr(actRoundNr + 1);
		}
		
		return nextRound;
	}
	
	/**
	 * Method to get the previous round for users to show them 
	 * which decisions they made before and also which results they had.
	 * @return the previous round to show the users the previous decisions
	 */
	public Round getPreviousRound(){
		
		//first get actual round + roundNr
		//if round==null then game not started
		//if roundNr==1 then first round and no previous Round
		//if roundNr>1 then there should be a previous round
		
		Round previousRound = null;
		Round actRound = this.getConfigurableRound();
		int actRoundNr = 0;
		
		if(actRound!=null){
			actRoundNr = actRound.getRoundNumber();
			if(actRoundNr>1){
				previousRound = this.getRoundWithNr(actRoundNr-1);
			}
		}
		
		return previousRound;
	}
	
	/**
	 * Method to get the round for the transferred roundNr.
	 * @return the round for the NrParameter	 
	 */
	public Round getRoundWithNr(int roundNr){
		Round round=null;
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(r.getRoundNumber()==roundNr){
					round=r;
					break;
				}
			}		
		}
		return round;
	}
	
	/**
	 * Method to get the actual configurable/playable round.
	 * This is the round where the users can make decisions.
	 * @return the actual configurable Round
	 */
	public Round getConfigurableRound(){
		Round round=null;
		Date now=new Date();
		
		if(this.rounds!=null){
			for(Round r : this.rounds){
				if(!r.isPlayed() && now.after(r.getStartDate()) && now.before(r.getEndDate())){
					round=r;
					break;
				}
			}		
		}
		return round;
	}
	
	public void addRound(Round round){
		if(this.rounds==null){
			this.rounds=new ArrayList<Round>();
		}
		
		this.rounds.add(round);
	}
	
	public void addUser(User user){
		if(this.users==null){
			this.users=new HashSet<User>();
		}
		
		this.users.add(user);
	}
	
	
	public void removeRound(Round round){
		if(rounds==null)
			rounds=new ArrayList<Round>();
		
		rounds.remove(round);	
	}
	
	
	public void removeUser(User user){
		if(users==null)
			users=new HashSet<User>();
		users.remove(user);	
		this.getUsers();
	}
	
	public boolean isFinished(){
		boolean finished=true;
		
		for(Round round : rounds){
			if(!round.isPlayed()){
				finished=false;
				break;
			}
		}
		
		return finished;
	}
	
	@Override
	public String toString() {
		return "Game [name=" + name + ", initDate=" + initDate
				+ ", roundTime=" + roundTime + "]";
	}

	
}
